package com.zhitan.netty.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.ResourceLeakDetector;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.net.InetSocketAddress;

/**
 * Netty 服务端
 **/
@Slf4j
@Component
public class NettyServer implements CommandLineRunner {
    /**
     * boss 线程组用于处理连接工作
     */
    private EventLoopGroup bossLoopGroup;
    /**
     * work 线程组用于数据处理
     */
    private EventLoopGroup workLoopGroup;
    @Value("${netty.port}")
    private Integer port;
    @Resource
    private NettyServerChannelInitializer nettyServerChannelInitializer;

    /**
     * 启动工作
     */
    private void start() throws InterruptedException {
        try {
            InetSocketAddress address = new InetSocketAddress("0.0.0.0", port);
            // 创建服务端的启动对象，设置参数
            bossLoopGroup = new NioEventLoopGroup();
            workLoopGroup = new NioEventLoopGroup();
            ServerBootstrap bootstrap = new ServerBootstrap();
            // 设置两个线程组boosGroup和workerGroup
            bootstrap.group(bossLoopGroup, workLoopGroup)
                    // 设置服务端通道实现类型
                    .channel(NioServerSocketChannel.class)
                    .localAddress(address)
                    // 编码解码
                    .childHandler(nettyServerChannelInitializer)
                    // 服务端接受连接的队列长度，如果队列已满，客户端连接将被拒绝
                    .option(ChannelOption.SO_BACKLOG, 1024 * 1024)
                    // 保持长连接，2小时无数据激活心跳机制
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    // 通过NoDelay禁用Nagle,使消息立即发出去，不用等待到一定的数据量才发出去
                    .childOption(ChannelOption.TCP_NODELAY, true);
            // 设置资源检测级别，用于得知内存泄漏位置
            ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.ADVANCED);
            // 绑定端口，开始接收进来的连接
            ChannelFuture future = bootstrap.bind(address).sync();
            if (future.isSuccess()) {
                log.info("Netty服务器开始监听端口：{} 成功", port);
            } else {
                log.error("Netty服务器开始监听端口：{} 失败", port);
            }
            // 关闭channel和块，直到它被关闭
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
            bossLoopGroup.shutdownGracefully().sync();
            workLoopGroup.shutdownGracefully().sync();
        }
    }

    @PreDestroy
    public void destroy() throws InterruptedException {
        bossLoopGroup.shutdownGracefully().sync();
        workLoopGroup.shutdownGracefully().sync();
        log.info("Netty Server关闭了");
    }

    /**
     * 异步延迟启动
     */
    @Async
    @Override
    public void run(String... args) throws Exception {
        start();
    }
}
